﻿using System;
using System.Text;
using COC.Algorithm;
using COC.Character.Extra;
using COC.Threading;
using COC.Tools.Pathing.AIViewModel;
using UnityEngine;
using System.Collections.Generic;
using System.Collections;
using Random = UnityEngine.Random;
namespace COC.Tools.Pathing.Test
{
    public class EnchanceMap : MonoBehaviour
    {
        private GameObject container;
        private GameObject indicators;

        #region graph information
        string prefabeTile = "tile";
        private CTile _tile;
        private float rows = 100.0f;
        private float columns = 100.0f;
        private float tileSize = 10.0f;
        private float offset = .5f;
        List<CTile> allTiles = new List<CTile>();
        GridNode[,] avaliableGraph = null; //filter avaliable graph when have some obstacle on map.
        #endregion
        #region obstacle information

        CTile[,] mObstacleMap = null;
        #endregion
        CTile[] BHVArray = null;
        CTile[,] tileMap = null;

        #region a* aStarPathFinding
        AIPathFinding aStarPathFinding;
        int followerPeopleId = 1;
        #endregion

        #region theme information

        static Color color = new Color(.114f, .319f, .255f, .9f);
        static Color color3 = new Color(.614f, .619f, .655f, .5f);
        static Color color2 = new Color(.614f, .719f, .255f, .9f);
        static Color color4 = new Color(.994f, .990f, .825f, .5f);
        static Color color5 = new Color(.914f, .561f, .145f, .6f);
        static Color color6 = new Color(.1014f, .971f, .255f, .5f);

        #endregion
        #region ai
        private Vector3 __targetLocalPosition = new Vector3(188.34f, characterHeightPositon, 126.78f);
        [HideInInspector]
        public static float characterHeightPositon = 1.5f;
        float[,] followersPos = new float[,] { { 29.34f, characterHeightPositon, 16.78f }, { 39.34f, characterHeightPositon, 16.78f }, { 49.34f, characterHeightPositon, 16.78f }, { 59.34f, characterHeightPositon, 16.78f }, { 69.34f, characterHeightPositon, 16.78f }, { 79.34f, characterHeightPositon, 16.78f }, { 89.34f, characterHeightPositon, 16.78f }, { 99.34f, characterHeightPositon, 16.78f } };
        float[,] targetsPos = new float[,] { { 298.34f, characterHeightPositon, 106.78f }, { 178.34f, characterHeightPositon, 126.78f }, { 168.34f, characterHeightPositon, 116.78f }, { 158.34f, characterHeightPositon, 106.78f }, { 148.34f, characterHeightPositon, 96.78f }, { 158.34f, characterHeightPositon, 106.78f }, { 18.34f, characterHeightPositon, 96.78f }, { 148.34f, characterHeightPositon, 116.78f } };
        Color[] followerColors = new Color[] { color, color3, color4, color6, color5, color2, color3, color5, color6, color4, color };

        #endregion
        #region object pool
        PoolManager<String, Component> singleObjectPool;
        CThreadPool threadPool;

        #endregion
        String PATH_THREAD_POOL = "path_pool_";


        void Awake()
        {
            // boottrapThreadPool();
            StartCoroutine(createRequirements());
            GameObject go = new GameObject();
            go.name = PathFindingConstant.OBJECT_POOL_SYMBOL;
            go.tag = PathFindingConstant.TAG.POOL_MANAGEMENT_TAG;
            go.AddComponent<ObjectPool>().enabled = true;
            DontDestroyOnLoad(go);

            GameObject path_finding = new GameObject();
            path_finding.name = PathFindingConstant.PATH_FINDING_THREAD_POOL;
            path_finding.AddComponent<AIPathFinding>();
            singleObjectPool = ObjectPool.getInstance();
            var item = UnityEngine.Object.Instantiate(Resources.Load(Characters.Resource.Path.MAP_TILE)) as GameObject;
            var copy = UnityEngine.Object.Instantiate(Resources.Load(Characters.Resource.Path.ARCHER)) as GameObject;
            putDefaultObjectTOPool(Characters.ARCHER, copy);
            putDefaultObjectTOPool(Characters.MAP_TILE, item);


        }

        /// <summary>
        /// start thread pool which execute how to find path.
        /// </summary>
        private void boottrapThreadPool()
        {
            CThreadPool.CThreadPoolInfo tpOption = new CThreadPool.CThreadPoolInfo();
            tpOption.isBackgound = true;
            tpOption.mPriority = CThreadPool.CThread.CThreadOption.Policy.DEFAULT;
            CEventQueue doQueue = new CEventQueue();
            CEventQueue futureQueue = new CEventQueue();
            threadPool = new CThreadPool(PATH_THREAD_POOL, 4, doQueue, futureQueue, null, tpOption);
            threadPool.Start();

        }

        private void putDefaultObjectTOPool(String type, GameObject go)
        {
            singleObjectPool.addPrefab(type, go.transform);

        }

        void Start()
        {
            Font ArialFont = (Font)Resources.GetBuiltinResource(typeof(Font), "Arial.ttf");

            container = new GameObject("grid");
            tileMap = new CTile[(int)rows, (int)columns];
            mObstacleMap = new CTile[(int)rows, (int)columns];

            int index = 0;
            indicators = new GameObject("indexText");
            indicators.transform.localScale = new Vector3(.98f, characterHeightPositon, .98f);
            GameObject item = null;
            //generating grid
            Shader shader = Shader.Find("Transparent/Diffuse");
            GameObject indexText = null;
            for (int i = 1; i <= rows; i++)
            {
                for (int j = 1; j <= columns; j++)
                {

                    /*  item=singleObjectPool.Spawn(Characters.MAP_TILE).gameObject;
                      item.name = "item_" + (i - 1) + "_" + (j - 1);
                      item.transform.localPosition = new Vector3(i * (tileSize + offset), 1.0f, j * (tileSize + offset));
                      item.transform.localScale = new Vector3(tileSize, 1.0f, tileSize);
                      item.transform.parent = container.transform;
                      item.renderer.material.color = Color.Lerp(color, color2, 3.0f * Time.deltaTime);
                      item.renderer.material.shader = shader;*/
                    /* _tile = new CTile(item.transform.localPosition, item.transform);

                     _tile.index = index++;
                     _tile.g_value = i + j;
                     _tile.pointX = i;
                     _tile.pointY = j;*/
                    //show index
                    //indexText = singleObjectPool.Spawn(Characters.MAP_TILE).gameObject;
                    /*  indexText.name = "item" + i + "_" + j + "_index";
                      indexText.transform.parent = indicators.transform;
                      indexText.transform.localPosition = new Vector3(i * (tileSize + offset), 1.2f, j * (tileSize + offset) + tileSize / 2);
                      indexText.transform.localRotation = Quaternion.AngleAxis(90, Vector3.right);
                      indexText.transform.localScale = new Vector3(tileSize, tileSize, tileSize);
                      indexText.AddComponent<MeshRenderer>();
                      indexText.renderer.material = ArialFont.material;
                       TextMesh tm=null;
                      if (indexText.GetComponent<TextMesh>() == null)
                      {
                        
                      tm = indexText.AddComponent<TextMesh>();
                      }
                      tm=indexText.GetComponent<TextMesh>();
                      tm.text = "[" + (i - 1) + "," + (j - 1) + "]";
                      tm.characterSize = 0.25f;
                      tm.font = ArialFont;
                      tm.color = Color.red;*/
                    mObstacleMap[i - 1, j - 1] = new CTile();
                    mObstacleMap[i - 1, j - 1].walkable = GraphNode.State.NONE;
                    //  tileMap[i - 1, j - 1] = _tile;
                }
            }
            /* for (int i = 0; i < tileMap.GetLength(0); i++)
             {
                 for (int j = 0; j < tileMap.GetLength(1); j++)
                 {
                     allTiles.Add(tileMap[i, j]);
                 }
             }*/
            /*  BHVArray = allTiles.ToArray();
              CTile temp = null;
              int middle = 1;
              int numberOfOpenListItems = 0;
              int p;
              for (p = 1; p <= BHVArray.Length; p++)
              {
                  if (p * 2 + 1 < BHVArray.Length - 1 && Math.Abs(p % middle) == 0)
                  {

                      if (BHVArray[p * 2 - 1].g_value > BHVArray[p * 2].g_value)
                      {
                          temp = BHVArray[p * 2 - 1];
                          BHVArray[p * 2 - 1] = BHVArray[p * 2];
                          BHVArray[p * 2] = temp;

                      }
                      if (BHVArray[p * 2].g_value > BHVArray[p * 2 + 1].g_value)
                      {
                          temp = BHVArray[p * 2 + 1];
                          BHVArray[p * 2 + 1] = BHVArray[p * 2];
                          BHVArray[p * 2] = temp;
                      }
                      middle *= 2;
                  }


              }*/

            //  for (int k = 0; k < BHVArray.Length; k++)
            // {
            //   UnityEngine.Debug.Log("index[" + BHVArray[k].index + "] g value=" + BHVArray[k].g_value);
            // }
            //set some obstacle
            GameObject obstacleObj = null;
            Vector3 obstacleTilePos = Vector3.zero;
            GameObject obstacles = new GameObject();
            obstacles.name = "Obstacles";
            obstacles.transform.localScale = new Vector3(obstacles.transform.localScale.x, .97f, obstacles.transform.localScale.z);
            //obstacles.transform.parent = container.transform;
            //   p = 0;
            int k = 0;
            int targetRow = (int)(__targetLocalPosition.x / (tileSize + offset));
            int targetCol = (int)(__targetLocalPosition.z / (tileSize + offset));

            mObstacleMap = calculateObstacleLayer(obstacles, mObstacleMap, targetRow, targetCol);

            aStarPathFinding = FindObjectOfType<AIPathFinding>();
            aStarPathFinding.Setup(true, 5, (int)rows, (int)columns, tileSize, offset, mObstacleMap);
            aStarPathFinding.Lunch();

            //aStarPathFinding.mBHVArray = BHVArray;
            // aStarPathFinding.mOffset = offset;
            //aStarPathFinding.mTileSize = tileSize;
            // aStarPathFinding.mAnsestor = container.transform;
            //aStarPathFinding.mRows = (int) rows;
            //aStarPathFinding.mColumns = (int) columns;
            //aStarPathFinding.mHeight = characterHeightPositon;
            //aStarPathFinding.mObstacleMap = mObstacleMap;
            //aStarPathFinding.gridNodes = avaliableGraph;
            GameObject anotherPerson;
            Vector3 followerPos = Vector3.zero;
            Vector3 targetPos = Vector3.zero;
            int randomNum = 0;
            int randomNum2 = 0;

            /*  for (int i = 0; i < followersPos.GetLength(0); i++)
              {
                      //follower
                  randomNum = Random.Range(1, avaliableGraph.Length);
                  randomNum2 = Random.Range(1, avaliableGraph.Length);
                  while (randomNum==randomNum2)
                  {
                      randomNum2 = Random.Range(1, avaliableGraph.Length);
                  }
                  followersPos[i, 0] = avaliableGraph[randomNum, randomNum2].x;
                  followersPos[i, 2] = avaliableGraph[randomNum, randomNum2].y;
                      //target
                  targetsPos[i, 0] = avaliableGraph[randomNum2,randomNum].x;
                  targetsPos[i, 2] = avaliableGraph[randomNum2,randomNum].y;

             
              }*/
            for (int i = 0; i < followersPos.GetLength(0); i++)
            {
                float x = followersPos[i, 0];
                float y = followersPos[i, 1];
                float z = followersPos[i, 2];
                float t_x = targetsPos[i, 0];
                float t_y = targetsPos[i, 1];
                float t_z = targetsPos[i, 2];
                followerPos = new Vector3(x, y, z);
                targetPos = new Vector3(t_x, t_y, t_z);
                UnityEngine.Debug.Log("[[[[[[[[[[[[[[[[[running]]]]]]]]]]]]]]]]]");
                simulatingAI(out anotherPerson, followerPos, followerColors[i], targetPos, i, aStarPathFinding);
            }
                allowRandom = true;
            



        }

        public bool allowRandom = false;
        int instanceCount = 0; 
        private void simulatingAI(out GameObject anotherPerson, Vector3 followerPos, Color followerTheme, Vector3 targetPos, int index, AIPathFinding _pathfinding)
        {
            //follower
            GameObject onePerson = GameObject.CreatePrimitive(PrimitiveType.Capsule);
            MovePath movePath = onePerson.AddComponent<MovePath>();
            movePath.findingPath = _pathfinding;
            movePath.id = followerPeopleId + index;
            movePath.mTileSize = tileSize;
            movePath.mOffset = offset;
            onePerson.name = "follower_" + index;
            onePerson.transform.localPosition = followerPos;
            onePerson.transform.localScale = new Vector3(tileSize / 2, .11f, tileSize / 2);
            onePerson.renderer.material.color = followerTheme;
            int currRow = (int)(onePerson.transform.localPosition.x / (tileSize + offset)) + 1;
            int currCol = (int)(onePerson.transform.localPosition.z / (tileSize + offset)) + 1;
            movePath.startingX = Mathf.Round((currRow * (tileSize + offset)));
            movePath.startingZ = Mathf.Round((currCol * (tileSize + offset)));
            movePath.speed = 1.0f;
            //target person
            anotherPerson = GameObject.CreatePrimitive(PrimitiveType.Capsule);
            anotherPerson.name = "targetPerson" + index;

            anotherPerson.transform.localPosition = targetPos;
            anotherPerson.transform.localScale = new Vector3(tileSize / 2, .11f, tileSize / 2);
            anotherPerson.renderer.material.color = new Color(.910f, .366f, .209f, .5f);
            movePath.targetTransform = anotherPerson.transform;
            movePath.schedulePath(onePerson.transform.localPosition, anotherPerson.transform.localPosition);
            StartCoroutine(RandomInstantiateGM(onePerson.transform));
            return;
        }


        private CTile[,] calculateObstacleLayer(GameObject obstacles, CTile[,] obstacleMap, int targetRow, int targetCol)
        {
            generateObstacle(obstacles, 2, 3, 1, 9, obstacleMap, targetRow, targetCol);
            generateObstacle(obstacles, 10, 9, 11, 8, obstacleMap, targetRow, targetCol);
            generateObstacle(obstacles, 6, 1, 10, 5, obstacleMap, targetRow, targetCol);
            // generateObstacle(obstacles, 11, 3, 2, 4, obstacleMap, targetRow, targetCol);
            //  generateObstacle(obstacles, 11, 10, 2, 2, obstacleMap, targetRow, targetCol);
            StringBuilder strBuilder = new StringBuilder();
            int count2, count1 = 0;
            strBuilder.Append("this is unwalkable positon: { ");
            for (int i = 0; i < obstacleMap.GetLength(0); i++)
            {
                for (int j = 0; j < obstacleMap.GetLength(1); j++)
                {
                    if (obstacleMap[i, j].walkable == GraphNode.State.UNWALKABLE)
                    {
                        strBuilder.Append("obstacleMap[" + i + "," + j + "],");
                        count1++;
                    }
                }
            }
            int graphNodeCount = (int)rows * (int)columns - count1;
            avaliableGraph = new GridNode[graphNodeCount, graphNodeCount];
            count1 = 0;
            count2 = 0;
            /* for (int x = 0; x < rows;x++ )
             {
                 for (int y = 0; y < columns;y++ )
                 {
                     if (obstacleMap[x, y].walkable != GraphNode.State.UNWALKABLE)
                     {
                         if (avaliableGraph[count1,count2]==null)
                             avaliableGraph[count1, count2] = new GridNode();
                         avaliableGraph[count1, count2].x = x;
                         avaliableGraph[count1, count2].y = y;
                         count1++;
                         count2++;

                     }
                 }
             }*/
            /*  StringBuilder b=new StringBuilder();
              b.Append("[AVALIABLEGRAPH]:");
              foreach(GridNode g in avaliableGraph)
              {
                  b.Append("[" + g.x + "," + g.y + "]");
              }*/
            //Debug.Log(b.ToString());
            string unwalkableInfos = strBuilder.ToString().Substring(0, strBuilder.Length - 1) + " }.";

            UnityEngine.Debug.Log("counts: " + count1 + "," + unwalkableInfos);
            return obstacleMap;
        }
        IEnumerator RandomInstantiateGM(Transform t)
        {
            while (true)
            {
                if (allowRandom)
                {
                    if (instanceCount < 1000)
                    {
                        GameObject.Instantiate(t);

                        instanceCount++;
                    }
                }
                yield return new WaitForSeconds(.8f);
            }
        }
        void FixedUpdate()
        {

        }
        private void generateObstacle(GameObject obstacles, int startXIndex, int startYIndex, int rowRangeScope, int colRangeScope, CTile[,] targeObstacleMap, int targetRow, int targetCol)
        {

            int p;
            int k;
            Vector3 obstacleTilePos;
            GameObject obstacleObj;
            for (p = startXIndex; p < rows - rowRangeScope - 1; p++)
            {
                var range = (int)Random.Range(1, rows - 4);
                int divisor = (range < 2) ? 2 : range;
                for (k = startYIndex; k < columns - colRangeScope - 1; k++)
                {
                    if (p < rows && k < columns && p != targetRow && k != targetCol)
                    {
                        if (targeObstacleMap[p - 1, k - 1].walkable != GraphNode.State.UNWALKABLE)
                        {

                            if (p % divisor == 1 || p % (divisor - 1) == 0)
                            {
                                obstacleTilePos = new Vector3(p * (tileSize + offset) - (offset + .05f), 1.6f, k * (tileSize + offset) - (offset + .05f));
                                //obstacleObj = GameObject.CreatePrimitive(PrimitiveType.Cube);
                                obstacleObj = singleObjectPool.Spawn(Characters.MAP_TILE).gameObject;
                                obstacleObj.transform.parent = obstacles.transform;
                                obstacleObj.transform.localPosition = obstacleTilePos;
                                obstacleObj.transform.localScale = new Vector3(tileSize - .5f, .03f, tileSize - .5f);
                                obstacleObj.name = "obstacleD_" + (p - 1) + "_" + (k - 1);
                                obstacleObj.renderer.material.color = Color.Lerp(color4, color6, 3.0f * Time.deltaTime);
                                targeObstacleMap[p - 1, k - 1].walkable = GraphNode.State.UNWALKABLE;
                                k++;
                                continue;

                            }
                            if (p % divisor == 0 || p % (divisor - 1) == 1)
                            {
                                obstacleTilePos = new Vector3(p * (tileSize + offset) - (offset + .05f), 1.6f, k * (tileSize + offset) - (offset + .05f));
                                // obstacleObj = GameObject.CreatePrimitive(PrimitiveType.Cube);
                                obstacleObj = singleObjectPool.Spawn(Characters.MAP_TILE).gameObject;
                                obstacleObj.transform.parent = obstacles.transform;
                                obstacleObj.transform.localPosition = obstacleTilePos;
                                obstacleObj.transform.localScale = new Vector3(tileSize - .5f, .03f, tileSize - .5f);
                                obstacleObj.name = "obstacleA_" + (p - 1) + "_" + (k - 1);
                                obstacleObj.renderer.material.color = Color.Lerp(color3, color6, 3.0f * Time.deltaTime);
                                targeObstacleMap[p - 1, k - 1].walkable = GraphNode.State.UNWALKABLE;
                                p++;
                                continue;


                            }
                            if (k % (divisor) == 0 || k % (divisor - 1) == 1)
                            {
                                obstacleTilePos = new Vector3(p * (tileSize + offset) - (offset + .05f), 1.6f, k * (tileSize + offset) - (offset + .05f));
                                // obstacleObj = GameObject.CreatePrimitive(PrimitiveType.Cube);
                                obstacleObj = singleObjectPool.Spawn(Characters.MAP_TILE).gameObject;
                                obstacleObj.transform.parent = obstacles.transform;
                                obstacleObj.transform.localPosition = obstacleTilePos;
                                obstacleObj.transform.localScale = new Vector3(tileSize - .5f, .03f, tileSize - .5f);
                                obstacleObj.name = "obstacleB_" + (p - 1) + "_" + (k - 1);
                                obstacleObj.renderer.material.color = Color.Lerp(color5, color2, 3.0f * Time.deltaTime);
                                targeObstacleMap[p - 1, k - 1].walkable = GraphNode.State.UNWALKABLE;
                                k++;

                                continue;

                            }
                            if (k % (divisor + 1) == 0 || k % (divisor) == 1)
                            {
                                obstacleTilePos = new Vector3(p * (tileSize + offset) - (offset + .05f), 1.6f, k * (tileSize + offset) - (offset + .05f));
                                //obstacleObj = GameObject.CreatePrimitive(PrimitiveType.Cube);
                                obstacleObj = singleObjectPool.Spawn(Characters.MAP_TILE).gameObject;
                                obstacleObj.transform.parent = obstacles.transform;
                                obstacleObj.transform.localPosition = obstacleTilePos;
                                obstacleObj.transform.localScale = new Vector3(tileSize - .5f, .03f, tileSize - .5f);
                                obstacleObj.name = "obstacleC_" + (p - 1) + "_" + (k - 1);
                                obstacleObj.renderer.material.color = Color.Lerp(color6, color2, 3.0f * Time.deltaTime);
                                targeObstacleMap[p - 1, k - 1].walkable = GraphNode.State.UNWALKABLE;
                                continue;

                            }
                        }

                    }
                }
            }

        }
        Vector3 mousePos;


        void OnGUI()
        {
            GUI.backgroundColor = Color.Lerp(color, color2, 3.0f * Time.deltaTime);
            GUILayout.BeginArea(new Rect(.0f, Screen.height - 120.0f, 130.0f, 140.0f));
            GUI.Box(new Rect(.0f, .0f, 100.0f, 140.0f), "Menu");
            if (GUI.Button(new Rect(.0f, 30.0f, 100.0f, 20.0f), "Move"))
            {

            }
            GUI.Button(new Rect(.0f, 60.0f, 100.0f, 20.0f), "Attack");
            GUI.Button(new Rect(.0f, 90.0f, 100.0f, 20.0f), "End Turn");
            GUILayout.EndArea();
            GUILayout.BeginArea(new Rect(Screen.height - 5.0f, 0.0f, 200.0f, 200.0f));
            GUILayout.Label("Count Of Instance :" + instanceCount);
            GUILayout.EndArea();
        }


        IEnumerator createRequirements()
        {
            /*  GameObject go = new GameObject();
              go.name = "ObjectsPool";
              go.tag = "Pool_Manager";
              go.AddComponent<ObjectPool>().enabled=true;
              DontDestroyOnLoad(go);
              Debug.Log("It's ok");*/
            yield return null;

        }
        void Destroy()
        {
            StopAllCoroutines();
        }
        void OnApplicationQuit()
        {
            {
                if (threadPool != null)
                {
                    threadPool.Cancel(true);
                    while (threadPool.isCancel())
                    {
                        threadPool = null;
                        UnityEngine.Debug.Log("Have already produced:" + instanceCount+" object.");
                        Destroy(this);
                    }
                }
            }
        }
    }
}